package com.zj.docker.controller;

import ch.qos.logback.core.net.SyslogOutputStream;
import com.google.common.collect.ImmutableSet;
import com.spotify.docker.client.DockerClient;
import com.spotify.docker.client.LogStream;
import com.spotify.docker.client.exceptions.DockerException;
import com.spotify.docker.client.messages.ContainerInfo;
import com.zj.docker.Repository.ContainersRepository;
import com.zj.docker.client.LogClient;
import com.zj.docker.client.UserClient;
import com.zj.docker.common.User;
import com.zj.docker.component.DockerComponent;
import com.zj.docker.component.LogComponent;
import com.zj.docker.component.UserRoleAuthentication;
import com.zj.docker.domain.Containers;
import com.zj.docker.enums.CommonEnum;
import com.zj.docker.enums.ContainerStatusEnum;
import com.zj.docker.enums.LogEnum;
import com.zj.docker.enums.RoleEnum;
import com.zj.docker.service.ContainersService;
import com.zj.docker.service.ImagesInfoService;
import com.zj.docker.utils.ListPagingUtils;
import com.zj.docker.utils.ResultVoUtil;
import com.zj.docker.vo.ResultVo;
import com.zj.docker.websocket.WebSocketServer;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Auther: zj
 * @Date: 2018/12/11 09:31
 * @Description: container controller
 */
@RestController
@Slf4j
public class ContainerController {
    @Autowired
    UserRoleAuthentication userRoleAuthentication;
    @Autowired
    UserClient userClient;
    @Autowired
    ImagesInfoService imagesInfoService;
    @Autowired
    ContainersService containersService;
    @Autowired
    DockerComponent docker;
    @Autowired
    WebSocketServer webSocketServer;

    @Autowired
    LogClient logClient;

    @Autowired
    LogComponent logComponent;

    @Value("${server.port}")
    private String serverPort;

    @Value( "${docker.server.address}" )
    private String dockerAddress;

    @Value( "${docker.server.port}" )
    private String dockerPort;


    /**
     * 创建容器，普通用户权限
     * @param request request
     * @param apiKey 密钥
     * @param imageId 镜像id
     * @param containerName 容器名
     * @param cmd 命令
     * @param env 环境变量
     * @param destination 容器内部的挂载目录
     * @return ResultVo
     */
    @PostMapping("/customer/createContainer")
    public ResultVo customerCreateContainer(HttpServletRequest request,
                                            @RequestParam(value = "apiKey") String apiKey,
                                            @RequestParam(value = "imageId") String imageId,
                                            @RequestParam(value = "containerName") String containerName,
                                            @RequestParam(value = "cmd",required = false,defaultValue ="") String[] cmd,
                                            @RequestParam(value = "env", required = false,defaultValue = "") String[] env,
                                            @RequestParam(value = "destination",required = false,defaultValue = "") String[] destination) {
        //鉴权
        String username = userRoleAuthentication.getUsernameAndAutenticateUserRoleFromRequest( request, RoleEnum.User.getMessage() );
        if (Objects.equals( username, CommonEnum.FALSE.getMessage())) {
            return ResultVoUtil.error();
        }
        //参数校验
        if(StringUtils.isBlank( apiKey )||StringUtils.isBlank(imageId)||StringUtils.isBlank( containerName )) {
            return ResultVoUtil.error(CommonEnum.PARAM_ERROR.getMessage());
        }
        //校验api-key的有效性
        User user = userClient.getUserByUsername( username );
        String sessionId =user.getWebsocketSessionId();
        if (!Objects.equals( apiKey, user.getApiKey() )) {
            return ResultVoUtil.error(CommonEnum.CONTAINER_APIKEY_ERROR.getMessage());
        }
        //获取镜像内部暴露接口
        ImmutableSet<String> exportPorts = imagesInfoService.listExportPorts(imageId);

        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
        String date = df.format(new Date());// new Date()为获取当前系统时间，也可使用当前时间戳

        System.out.println( request.getMethod() );
        System.out.println( request.getRequestURI() );

        String method = request.getMethod();
        String url = request.getRequestURI();
        String ip = getRemoteAddr( request);
        String userAgent = request.getHeader("user-agent");


        //异步执行,因为发现request会莫名其妙丢失，所以只能用来一种stupid的方法
        containersService.createContainer(username,imageId,cmd,exportPorts,containerName,env,destination,date,sessionId,
                                           method,
                                            url,
                                            ip,
                                            userAgent);

        return ResultVoUtil.success("开始创建容器！");

    }

    /**
     * 获取当前用户的所有容器,分页，普通用户权限
     * @param request request
     * @param pageIndex 页码
     * @return list
     */
    @GetMapping("/customer/listSelfAllContainers")
    public ResultVo customerListSelfAllContainers(HttpServletRequest request,
                                          @RequestParam(value = "pageIndex",required = false,defaultValue = "1") Integer pageIndex) {
        //鉴权
        String username = userRoleAuthentication.getUsernameAndAutenticateUserRoleFromRequest( request, RoleEnum.User.getMessage() );
        if (Objects.equals( username, CommonEnum.FALSE.getMessage())) {
            return ResultVoUtil.error();
        }
        List<Containers> list=containersService.listAllContainerByAuthorWithPage( username, pageIndex-1).getContent();
        return ResultVoUtil.success(list);
    }

    /**
     * 获取当前用户的所有容器的总数，普通用户权限
     * @param request
     * @return
     */
    @GetMapping("/customer/getSelfContainerTotal")
    public ResultVo customerGetSelfContainerTotal(HttpServletRequest request) {
        //鉴权
        String username = userRoleAuthentication.getUsernameAndAutenticateUserRoleFromRequest( request, RoleEnum.User.getMessage() );
        if (Objects.equals( username, CommonEnum.FALSE.getMessage())) {
            return ResultVoUtil.error();
        }
        List<Containers> list=containersService.listAllContainer( username);
        return ResultVoUtil.success(list.size());
    }

    /**
     * 启动容器，普通用户权限
     * @param request
     * @param containerId
     * @return
     * @throws DockerException
     * @throws InterruptedException
     */
    @GetMapping("/customer/startContainer")
    public ResultVo customerStartContainer(HttpServletRequest request,
                                           @RequestParam("containerId") String containerId) {
        //鉴权
        String username = userRoleAuthentication.getUsernameAndAutenticateUserRoleFromRequest( request, RoleEnum.User.getMessage() );
        if (Objects.equals( username, CommonEnum.FALSE.getMessage())) {
            return ResultVoUtil.error();
        }

        //参数的校验
        if(StringUtils.isBlank( containerId )) {
            return ResultVoUtil.error(CommonEnum.PARAM_ERROR.getMessage());
        }
        Containers container =containersService.findOneByContainerId( containerId );
        if (container==null) {
            return ResultVoUtil.error(CommonEnum.CONTAINER_FINDONEBYID_ERROR.getMessage());
        }

        //启动条件的判定
        if (Objects.equals( container.getStatus(), ContainerStatusEnum.START.getCode() )) {
            return ResultVoUtil.error("该容器已是启动状态！");
        }
        try {
            docker.getThisDockerClient().startContainer( containerId );

        } catch (Exception e) {
            log.error("启动容器状态出现异常，错误信息：{}",e.getMessage());
            //写入日志
            logComponent.saveLog( request,LogEnum.DOCKER_START_ERROR,e );
        }


        //同步数据库
        container.setStatus( ContainerStatusEnum.START.getCode() );
        containersService.saveOrUpdata( container );
        //写入日志
        logComponent.saveLog( request, LogEnum.DOCKER_START );

        return ResultVoUtil.success( "容器启动成功！" );
    }

    /**
     * 关闭容器，普通用户权限
     * @param request
     * @param containerId
     * @return
     * @throws DockerException
     * @throws InterruptedException
     */
    @GetMapping("/customer/stopContainer")
    public ResultVo customerStopContainer(HttpServletRequest request,
                                          @RequestParam("containerId") String containerId) throws DockerException, InterruptedException {
        //鉴权
        String username = userRoleAuthentication.getUsernameAndAutenticateUserRoleFromRequest( request, RoleEnum.User.getMessage() );
        if (Objects.equals( username, CommonEnum.FALSE.getMessage())) {
            return ResultVoUtil.error();
        }
        //参数的校验
        if(StringUtils.isBlank( containerId )) {
            return ResultVoUtil.error(CommonEnum.PARAM_ERROR.getMessage());
        }
        Containers container =containersService.findOneByContainerId( containerId );
        if (container==null) {
            return ResultVoUtil.error(CommonEnum.CONTAINER_FINDONEBYID_ERROR.getMessage());
        }
        //启动条件的判定
        if (Objects.equals( container.getStatus(), ContainerStatusEnum.STOP.getCode() )) {
            return ResultVoUtil.error("该容器已是关闭状态！");
        }
        docker.getThisDockerClient().stopContainer( containerId,5 );

        //同步数据库
        container.setStatus( ContainerStatusEnum.STOP.getCode() );
        containersService.saveOrUpdata( container );

        //写入日志
        logComponent.saveLog( request,LogEnum.DOCKER_STOP );

        return ResultVoUtil.success( "容器关闭成功！" );
    }

    /**
     * 强制停止容器，普通用户权限
     * @param request
     * @param containerId
     * @return
     * @throws DockerException
     * @throws InterruptedException
     */
    @GetMapping("/customer/killContainer")
    public ResultVo customerKillContainer(HttpServletRequest request,
                                          @RequestParam("containerId") String containerId) throws DockerException, InterruptedException {
        //鉴权
        String username = userRoleAuthentication.getUsernameAndAutenticateUserRoleFromRequest( request, RoleEnum.User.getMessage() );
        if (Objects.equals( username, CommonEnum.FALSE.getMessage())) {
            return ResultVoUtil.error();
        }
        //参数的校验
        if(StringUtils.isBlank( containerId )) {
            return ResultVoUtil.error(CommonEnum.PARAM_ERROR.getMessage());
        }
        Containers container =containersService.findOneByContainerId( containerId );
        if (container==null) {
            return ResultVoUtil.error(CommonEnum.CONTAINER_FINDONEBYID_ERROR.getMessage());
        }
        //启动条件的判定
        if (Objects.equals( container.getStatus(), ContainerStatusEnum.STOP.getCode() )) {
            return ResultVoUtil.error("该容器已是关闭状态！");
        }

        docker.getThisDockerClient().killContainer( containerId );

        //同步数据库
        container.setStatus( ContainerStatusEnum.STOP.getCode() );
        containersService.saveOrUpdata( container );

        logComponent.saveLog( request,LogEnum.DOCKER_STOP_STRONG );

        return ResultVoUtil.success( "容器强制关闭成功！" );
    }

    //重启容器，普通用户权限
    @GetMapping("/customer/restartContainer")
    public ResultVo customerRestartContainer(HttpServletRequest request,
                                             @RequestParam("containerId") String containerId) throws DockerException, InterruptedException {
        //鉴权
        String username = userRoleAuthentication.getUsernameAndAutenticateUserRoleFromRequest( request, RoleEnum.User.getMessage() );
        if (Objects.equals( username, CommonEnum.FALSE.getMessage())) {
            return ResultVoUtil.error();
        }
        //参数的校验
        if(StringUtils.isBlank( containerId )) {
            return ResultVoUtil.error(CommonEnum.PARAM_ERROR.getMessage());
        }
        Containers container =containersService.findOneByContainerId( containerId );
        if (container==null) {
            return ResultVoUtil.error(CommonEnum.CONTAINER_FINDONEBYID_ERROR.getMessage());
        }

        docker.getThisDockerClient().restartContainer( containerId );

        //同步数据库
        container.setStatus( ContainerStatusEnum.START.getCode() );
        containersService.saveOrUpdata( container );

        logComponent.saveLog( request,LogEnum.DOCKER_RESTART );

        return ResultVoUtil.success( "容器重启成功！" );

    }

    /**
     * 根据容器id，来获得容器的详细信息，普通用户
     * @param request
     * @param containerId
     * @return
     */
    @GetMapping("/customer/inspectContainer")
    public ResultVo customerInspectContainer(HttpServletRequest request,
                                             @RequestParam("containerId") String containerId) throws DockerException, InterruptedException {
        //鉴权
        String username = userRoleAuthentication.getUsernameAndAutenticateUserRoleFromRequest( request, RoleEnum.User.getMessage() );
        if (Objects.equals( username, CommonEnum.FALSE.getMessage())) {
            return ResultVoUtil.error();
        }
        //参数的校验
        if(StringUtils.isBlank( containerId )) {
            return ResultVoUtil.error(CommonEnum.PARAM_ERROR.getMessage());
        }
        Containers container =containersService.findOneByContainerId( containerId );
        if (container==null) {
            return ResultVoUtil.error(CommonEnum.CONTAINER_FINDONEBYID_ERROR.getMessage());
        }

        ContainerInfo containerInfo= docker.getThisDockerClient().inspectContainer( containerId );

        return ResultVoUtil.success(containerInfo);
    }


    /**
     * 删除容器，需要普通用户权限
     * @param request
     * @param containerId
     * @return
     * @throws DockerException
     * @throws InterruptedException
     */
    @GetMapping("/customer/removeContainer")
    public ResultVo customerRemoveContainer(HttpServletRequest request,
                                            @RequestParam("containerId") String containerId) throws DockerException, InterruptedException {

        //鉴权
        String username = userRoleAuthentication.getUsernameAndAutenticateUserRoleFromRequest( request, RoleEnum.User.getMessage() );
        if (Objects.equals( username, CommonEnum.FALSE.getMessage())) {
            return ResultVoUtil.error();
        }
        //参数的校验
        if(StringUtils.isBlank( containerId )) {
            return ResultVoUtil.error(CommonEnum.PARAM_ERROR.getMessage());
        }
        Containers container =containersService.findOneByContainerId( containerId );
        if (container==null) {
            return ResultVoUtil.error(CommonEnum.CONTAINER_FINDONEBYID_ERROR.getMessage());
        }
        //启动条件的判定
        if (!Objects.equals( container.getStatus(), ContainerStatusEnum.STOP.getCode() )) {
            return ResultVoUtil.error("容器还未关闭，不能删除！");
        }
        docker.getThisDockerClient().removeContainer( containerId );
        //同步数据库
        containersService.delete( container );
        logComponent.saveLog( request,LogEnum.DOCKER_DELETE );
        return ResultVoUtil.success( "容器删除成功！" );

    }

    /**
     * 获取容器日志信息 ,用户权限
     * @param request
     * @param containerId
     * @return
     * @throws DockerException
     * @throws InterruptedException
     */
    @GetMapping("/customer/getContainerLogs")
    public ResultVo customerGetContainerLogs(HttpServletRequest request,
                                             @RequestParam("containerId") String containerId) throws DockerException, InterruptedException {
        //鉴权
        String username = userRoleAuthentication.getUsernameAndAutenticateUserRoleFromRequest( request, RoleEnum.User.getMessage() );
        if (Objects.equals( username, CommonEnum.FALSE.getMessage())) {
            return ResultVoUtil.error();
        }
        //参数的校验
        if(StringUtils.isBlank( containerId )) {
            return ResultVoUtil.error(CommonEnum.PARAM_ERROR.getMessage());
        }
        Containers container =containersService.findOneByContainerId( containerId );
        if (container==null) {
            return ResultVoUtil.error(CommonEnum.CONTAINER_FINDONEBYID_ERROR.getMessage());
        }
        if(container.getStatus()!=1) {
            return ResultVoUtil.error( CommonEnum.CONTAINER_STATUS_HAS_OPEN.getMessage());
        }

        String logs;
        LogStream stream =docker.getThisDockerClient().logs( containerId, DockerClient.LogsParam.stdout(), DockerClient.LogsParam.stderr() );
        logs = stream.readFully();
//        System.out.println( logs );
        return ResultVoUtil.success(logs);
    }

    //todo 调用Terminal(容器终端)
    @PostMapping("/customer/terminal")
    public ResultVo showTerminal(HttpServletRequest request,
                                 String containerId,
                                 @RequestParam(defaultValue = "false") Boolean cursorBlink,
                                 @RequestParam(defaultValue = "100") Integer cols,
                                 @RequestParam(defaultValue = "50") Integer rows,
                                 @RequestParam(defaultValue = "100") Integer width,
                                 @RequestParam(defaultValue = "50") Integer height) {
        //鉴权
        String username = userRoleAuthentication.getUsernameAndAutenticateUserRoleFromRequest( request, RoleEnum.User.getMessage() );
        if (Objects.equals( username, CommonEnum.FALSE.getMessage())) {
            return ResultVoUtil.error();
        }
        //参数的校验
        if(StringUtils.isBlank( containerId )) {
            return ResultVoUtil.error(CommonEnum.PARAM_ERROR.getMessage());
        }
        Containers container =containersService.findOneByContainerId( containerId );
        if (container==null) {
            return ResultVoUtil.error(CommonEnum.CONTAINER_FINDONEBYID_ERROR.getMessage());
        }
        if(container.getStatus()!=1) {
            return ResultVoUtil.error( CommonEnum.CONTAINER_STATUS_HAS_OPEN.getMessage());
        }

        String url = "ws://localhost" + ":" + serverPort + "/ws/container/exec?width=" + width + "&height=" + height +
                "&ip=" + dockerAddress + "&port=" + dockerPort + "&containerId=" + containerId;

        Map<String, Object> map = new HashMap<>(16);
        map.put("cursorBlink", cursorBlink);
        map.put("cols", cols);
        map.put("rows", rows);
        map.put("url", url);


        return ResultVoUtil.success(map);

    }

    //==============================================>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    /**
     * 获取所有容器,分页，管理员权限
     * @param request request
     * @param pageIndex 页码
     * @return list
     */
    @GetMapping("/admin/listAllContainers")
    public ResultVo adminListAllContainers(HttpServletRequest request,
                                           @RequestParam(value = "pageIndex",required = false,defaultValue = "1") Integer pageIndex) {
        //鉴权
        String username = userRoleAuthentication.getUsernameAndAutenticateUserRoleFromRequest( request, RoleEnum.Admin.getMessage() );
        if (Objects.equals( username, CommonEnum.FALSE.getMessage())) {
            return ResultVoUtil.error();
        }
        List<Containers> list=containersService.listAllContainer();
        List<Object> list_new =ListPagingUtils.getPaging( list,pageIndex-1,5 );
        return ResultVoUtil.success(list_new);
    }

    /**
     * 获取当前用户的所有容器的总数，管理员权限
     * @param request
     * @return
     */
    @GetMapping("/admin/getAllContainerTotal")
    public ResultVo adminGetAllContainerTotal(HttpServletRequest request) {
        //鉴权
        String username = userRoleAuthentication.getUsernameAndAutenticateUserRoleFromRequest( request, RoleEnum.Admin.getMessage() );
        if (Objects.equals( username, CommonEnum.FALSE.getMessage())) {
            return ResultVoUtil.error();
        }
        List<Containers> list=containersService.listAllContainer();
        return ResultVoUtil.success(list.size());
    }

    /**
     * 删除容器，需要普通用户权限
     * @param request
     * @param containerId
     * @return
     * @throws DockerException
     * @throws InterruptedException
     */
    @GetMapping("/admin/removeContainer")
    public ResultVo adminRemoveContainer(HttpServletRequest request,
                                            @RequestParam("containerId") String containerId) throws DockerException, InterruptedException {

        //鉴权
        String username = userRoleAuthentication.getUsernameAndAutenticateUserRoleFromRequest( request, RoleEnum.Admin.getMessage() );
        if (Objects.equals( username, CommonEnum.FALSE.getMessage())) {
            return ResultVoUtil.error();
        }
        //参数的校验
        if(StringUtils.isBlank( containerId )) {
            return ResultVoUtil.error(CommonEnum.PARAM_ERROR.getMessage());
        }
        Containers container =containersService.findOneByContainerId( containerId );
        if (container==null) {
            return ResultVoUtil.error(CommonEnum.CONTAINER_FINDONEBYID_ERROR.getMessage());
        }
        //启动条件的判定
        if (!Objects.equals( container.getStatus(), ContainerStatusEnum.STOP.getCode() )) {
            return ResultVoUtil.error("容器还未关闭，不能删除！");
        }
        docker.getThisDockerClient().removeContainer( containerId );
        //同步数据库
        containersService.delete( container );

        logComponent.saveLog( request,LogEnum.DOCKER_DELETE );
        return ResultVoUtil.success( "容器删除成功！" );

    }

    @Autowired
    ContainersRepository containersRepository;

    @GetMapping("/test1")
    public ResultVo test1(HttpServletRequest request) {
        //鉴权
        String username = userRoleAuthentication.getUsernameAndAutenticateUserRoleFromRequest( request, RoleEnum.User.getMessage() );
        if (Objects.equals( username, CommonEnum.FALSE.getMessage())) {
            return ResultVoUtil.error();
        }

        List<Containers> containers =containersRepository.findAll();
        List<Object>list = ListPagingUtils.getPaging( containers,1,3 );
        return ResultVoUtil.success(list);
    }

    public static String getRemoteAddr(HttpServletRequest request) {
        String remoteAddr = request.getHeader("X-Real-IP");
        if (!StringUtils.isBlank(remoteAddr)) {
            remoteAddr = request.getHeader("X-Forwarded-For");
        } else if (!StringUtils.isBlank(remoteAddr)) {
            remoteAddr = request.getHeader("Proxy-Client-IP");
        } else if (!StringUtils.isBlank(remoteAddr)) {
            remoteAddr = request.getHeader("WL-Proxy-Client-IP");
        }
        return remoteAddr != null ? remoteAddr : request.getRemoteAddr();
    }


}
